package clientManage

import (
	"encoding/json"
	"fmt"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gorilla/websocket"
	"sync"
	"ws/server/config"
	"ws/server/model"
)

// Subscription 表示一个频道的订阅关系，包含WebSocket连接和频道名称。
// Subscription represents a client subscription to a channel.
type Subscription struct {
	Client     *websocket.Conn // WebSocket连接
	Channel    string          // 频道名称
	TargetCode string          // 定向标识，允许为空  比如:userId-xxxxx
}

// SubscriptionManager 用于管理客户端订阅关系的实体。
// 它包含订阅映射和客户端连接映射，使用互斥锁来保证并发安全。
// SubscriptionManager manages subscriptions for channels.
type SubscriptionManager struct {
	subs       map[string][]*Subscription // Maps channel names to lists of subscriptions.
	mutex      sync.RWMutex               // Mutex for concurrent access to subs.
	clients    map[*websocket.Conn]bool   // Maintains a list of all connected clients.
	clientsMtx sync.Mutex                 // Mutex for concurrent access to clients.
}

// NewSubscriptionManager 创建并返回一个新的SubscriptionManager实例。
// NewSubscriptionManager creates a new SubscriptionManager.
func NewSubscriptionManager() *SubscriptionManager {
	return &SubscriptionManager{
		subs:    make(map[string][]*Subscription),
		clients: make(map[*websocket.Conn]bool),
	}
}

// 创建一个全局的SubscriptionManager实例
var sm = NewSubscriptionManager()

// AddClient 将一个新的客户端连接添加到管理器的客户端列表中。
// AddClient adds a new client to the list of connected clients.
func AddClient(conn *websocket.Conn) {
	sm.clientsMtx.Lock()
	defer sm.clientsMtx.Unlock()
	sm.clients[conn] = true
	g.Log().Info(config.Ctx, "客户端建立链接", conn.RemoteAddr())
}

// RemoveClient 从管理器的客户端列表中移除一个客户端连接，
// 并同时取消该客户端的所有订阅关系。
// RemoveClient removes a client from the list of connected clients and cancels its subscriptions.
func RemoveClient(conn *websocket.Conn) {
	sm.clientsMtx.Lock()
	defer sm.clientsMtx.Unlock()
	delete(sm.clients, conn)
	// Cancel subscriptions for this client.
	sm.mutex.Lock()
	defer sm.mutex.Unlock()
	for channel, _ := range sm.subs {
		for i := len(sm.subs[channel]) - 1; i >= 0; i-- {
			if sm.subs[channel][i].Client == conn {
				// 移除并保留剩余部分
				sm.subs[channel] = append(sm.subs[channel][:i], sm.subs[channel][i+1:]...)
			}
		}
		if len(sm.subs[channel]) == 0 {
			delete(sm.subs, channel)
			g.Log().Info(config.Ctx, fmt.Sprintf("Deleted channel %s as it has no more subscribers.", channel))
		}
	}
	printSubscribers()
}

// Subscribe 将一个客户端订阅到指定的频道。
// Subscribe allows a client to subscribe to a channel.
func Subscribe(conn *websocket.Conn, channel string, targetCodes []string) {
	sm.mutex.Lock()
	defer sm.mutex.Unlock()
	// 检查targetCodes是否为空
	if len(targetCodes) == 0 {
		// 检查是否有来自相同连接且TargetCode为空的订阅项
		existingEmptyTargetCodeForConn := false
		for _, sub := range sm.subs[channel] {
			if sub.Client == conn && sub.TargetCode == "" {
				existingEmptyTargetCodeForConn = true
				break
			}
		}
		//log.Println(existingEmptyTargetCodeForConn)
		if !existingEmptyTargetCodeForConn {
			// 如果没有TargetCode为空的订阅，则添加一个
			sm.subs[channel] = append(sm.subs[channel], &Subscription{Client: conn, Channel: channel, TargetCode: ""})
		} else {
			g.Log().Info(config.Ctx, fmt.Sprintf("Client %v attempted to subscribe to channel %s with an empty targetCodes list, and an empty TargetCode subscription already exists.", conn.RemoteAddr(), channel))
		}
	} else {
		for _, targetCode := range targetCodes {
			for _, sub := range sm.subs[channel] {
				if sub.Client == conn && sub.TargetCode == targetCode {
					g.Log().Info(config.Ctx, fmt.Sprintf("Client %v attempted to subscribe to channel %s with targetCode %s, but an identical subscription already exists.", conn.RemoteAddr(), channel, targetCode))
					return
				}
			}
			sm.subs[channel] = append(sm.subs[channel], &Subscription{Client: conn, Channel: channel, TargetCode: targetCode})
		}
	}
	printSubscribers()
}

// Unsubscribe 取消一个客户端对指定频道的订阅。
// Unsubscribe allows a client to unsubscribe from a channel.
func Unsubscribe(conn *websocket.Conn, channel string, targetCodes []string) {
	sm.mutex.Lock()
	defer sm.mutex.Unlock()
	// 检查targetCodes是否为空
	if len(targetCodes) == 0 {
		for i := len(sm.subs[channel]) - 1; i >= 0; i-- {
			if sm.subs[channel][i].Client == conn {
				// 移除并保留剩余部分
				sm.subs[channel] = append(sm.subs[channel][:i], sm.subs[channel][i+1:]...)
			}
		}
	} else {
		for _, targetCode := range targetCodes {
			for i := len(sm.subs[channel]) - 1; i >= 0; i-- {
				if sm.subs[channel][i].Client == conn && sm.subs[channel][i].TargetCode == targetCode {
					// 移除并保留剩余部分
					sm.subs[channel] = append(sm.subs[channel][:i], sm.subs[channel][i+1:]...)
					break
				}
			}
		}
	}
	// 如果该频道没有订阅者了，删除它
	if len(sm.subs[channel]) == 0 {
		delete(sm.subs, channel)
		g.Log().Info(config.Ctx, fmt.Sprintf("Deleted channel %s as it has no more subscribers.", channel))
	}
	printSubscribers()
}

// BroadcastToChannel 向指定频道的所有订阅者发送消息。
// 如果存在订阅者，则遍历并发送消息；如果发送失败，记录日志。
// BroadcastToChannel sends a message to all subscribers of a specific channel.
func BroadcastToChannel(channel string, mt int, message interface{}, targetCodes []string) error {
	//log.Println("根据通道发送消息：", message)
	var byteMessage []byte
	switch v := message.(type) {
	case string:
		byteMessage = []byte(v)
	case []byte:
		byteMessage = v
	case model.MsgObject: // 添加对 消息类型的支撑
		jsonBytes, err := json.Marshal(v)
		if err != nil {
			g.Log().Error(config.Ctx, "Error marshaling map to JSON", err)
			return err
		}
		byteMessage = jsonBytes
	default:
		g.Log().Info(config.Ctx, "Unsupported message type:", v)
		return fmt.Errorf("unsupported message type: %T", message)
	}

	sm.mutex.RLock()
	defer sm.mutex.RUnlock()

	if subs, ok := sm.subs[channel]; ok {
		for _, sub := range subs {
			// 检查订阅者的目标代码是否在目标代码列表中
			if len(targetCodes) > 0 { // 确保目标代码列表不为空
				for _, tc := range targetCodes {
					if tc == sub.TargetCode {
						// 发送消息到匹配的客户端
						if err := sub.Client.WriteMessage(mt, byteMessage); err != nil {
							g.Log().Error(config.Ctx, "Error sending message to client:", err)
						} else {
							g.Log().Info(config.Ctx, "Message sent to client with TargetCode: %s", sub.TargetCode)
						}
						break // 匹配到一个就跳出循环，避免给同一客户端重复发送
					}
				}
			} else {
				// 如果目标代码列表为空，则向所有订阅者发送
				if err := sub.Client.WriteMessage(mt, byteMessage); err != nil {
					g.Log().Error(config.Ctx, "Error sending message to client:", err)
				} else {
					g.Log().Info(config.Ctx, "Message sent to client with TargetCode: %s", sub.TargetCode)
				}
			}
		}
	}
	return nil
}

// printSubscribers 用于调试目的，打印当前的所有订阅关系。
// printSubscribers prints the current subscriptions for debugging purposes.
func printSubscribers() {
	g.Log().Info(config.Ctx, "Current subscriptions:")
	for channel, subs := range sm.subs {
		g.Log().Info(config.Ctx, fmt.Sprintf("Channel: %s", channel))
		for _, sub := range subs {
			g.Log().Info(config.Ctx, fmt.Sprintf("\tClient: TargetCode=%s, RemoteAddr=%s", sub.TargetCode, sub.Client.RemoteAddr()))
		}
	}
}
